package com.spacex.danhuang.polive.activities;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;

import com.spacex.danhuang.polive.Application.VApplication;
import com.spacex.danhuang.polive.R;
import com.voiceengine.NTAudioRecord;

import org.daniulive.smartpublisher.SmartPublisherJni;

import java.io.IOException;
import java.util.List;

import io.socket.client.Socket;


@SuppressWarnings("deprecation")
public class MediaPublishActivity extends ChatRoomActivity implements SurfaceHolder.Callback, View.OnClickListener, Camera.PreviewCallback {
    private static final String TAG = "MediaPublishActivity";

    private VApplication app;
    private Socket mSocket;

    private int mUserRole;
    private String mPoUserid;

    NTAudioRecord audioRecord = null;	//for audio capture
    private SmartPublisherJni libPublisher = null;

    private SurfaceView mSurfaceView = null;
    private Button bt_switch_camera;
    private SurfaceHolder mSurfaceHolder = null;

    private Camera mCamera = null;
    private AutoFocusCallback myAutoFocusCallback = null;

    private boolean mPreviewRunning = false;

    private boolean isStart = false;

    private String publishURL;
    final private String baseURL = "rtmp://daniulive.com:1935/hls/stream_";

    private static final int FRONT = 1;		//前置摄像头标记
    private static final int BACK = 2;		//后置摄像头标记
    private int currentCameraType = FRONT;	//当前打开的摄像头标记
    private static final int PORTRAIT = 1;	//竖屏
    private static final int LANDSCAPE = 2;	//横屏
    private int currentOrigentation = PORTRAIT;

    private int curCameraIndex = -1;

    private int videoWidth;
    private int videoHeight;

    private int frameCount = 0;

    static {
        System.loadLibrary("SmartPublisher");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_publish);

        app = (VApplication)getApplication();
        mSocket = app.getSocket();
        mUserRole = app.getUserRole();
        mPoUserid = app.getUserId();

        initView();

        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        startPublish();
    }

    private void initView() {
        bt_switch_camera = (Button)this.findViewById(R.id.bt_media_publish_switch);
        bt_switch_camera.setOnClickListener(this);

        mSurfaceView = (SurfaceView)this.findViewById(R.id.sv_media_publish);
        if (mSurfaceView != null) {
            mSurfaceHolder = mSurfaceView.getHolder();
        }
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        setResolution(800, 480);

        mSurfaceView.getHolder().setKeepScreenOn(true); // 保持屏幕高亮

        //自动聚焦变量回调
        myAutoFocusCallback = new AutoFocusCallback() {
            public void onAutoFocus(boolean success, Camera camera) {
                if(success)//success表示对焦成功
                {
                    Log.i(TAG, "onAutoFocus succeed...");
                }
                else
                {
                    Log.i(TAG, "onAutoFocus failed...");
                }
            }
        };

        libPublisher = new SmartPublisherJni();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_media_publish_switch:
                Log.i(TAG, "Switch camera..");
                try {
                    switchCamera();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                break;
            default:
                break;
        }
    }

    private void setResolution(int width, int height) {
        videoWidth = width;
        videoHeight = height;

        initCamera(mSurfaceHolder);
    }

    private void startPublish() {
        if (isStart) {
            stop();
            return;
        }
        isStart = true;
        Log.i(TAG, "startPublish");

        if(libPublisher != null) {
            //publishURL = baseURL + mPoUserid;
            publishURL = baseURL;
            Log.i(TAG, publishURL);

            ConfigRecorderFuntion();

            Log.i(TAG, "videoWidth: "+ videoWidth + " videoHeight: " + videoHeight);

            libPublisher.SmartPublisherInit(videoWidth, videoHeight);

            int isStarted = libPublisher.SmartPublisherStartPublish(publishURL);
            if(isStarted != 0) {
                Log.e(TAG, "Failed to publish stream..");
            }
        }

        CheckInitAudioRecorder();
    }

    void CheckInitAudioRecorder() {
        if (audioRecord == null) {
            audioRecord = new NTAudioRecord(this, 1);
        }

        if (audioRecord != null) {
            Log.i(TAG, "onCreate, call executeAudioRecordMethod..");
            audioRecord.executeAudioRecordMethod();
        }
    }

    //Configure recorder related function.
    void ConfigRecorderFuntion() {
        if ( libPublisher != null ) {
            //默认不使用recoder
            if ( 0 != libPublisher.SmartPublisherSetRecorder(0) ) {
                Log.e(TAG, "SmartPublisherSetRecoder failed.");
                return;
            }
        }
    }

    private void stop()
    {
        Log.i(TAG, "onClick stop..");
        StopPublish();
        isStart = false;
    }

    private void SetCameraFPS(Camera.Parameters parameters) {
        if (parameters == null)
            return;

        int[] findRange = null;
        int defFPS = 20*1000;

        List<int[]> fpsList = parameters.getSupportedPreviewFpsRange();
        if (fpsList != null && fpsList.size() > 0) {
            for ( int i = 0; i < fpsList.size(); ++i ) {
                int[] range = fpsList.get(i);
                if (range != null && Camera.Parameters.PREVIEW_FPS_MIN_INDEX <  range.length && Camera.Parameters.PREVIEW_FPS_MAX_INDEX < range.length) {

                    Log.i(TAG, "Camera index:" + i + " support min fps:" + range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]);
                    Log.i(TAG, "Camera index:" + i + " support max fps:" + range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);

                    if (findRange == null) {
                        if (defFPS <= range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
                            findRange = range;

                            Log.i(TAG, "Camera found appropriate fps, min fps:" + range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]
                                    + " ,max fps:" + range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
                        }
                    }
                }
            }
        }

        if (findRange != null) {
            parameters.setPreviewFpsRange(findRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], findRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
        }

    }

    /*it will call when surfaceChanged*/
    private void initCamera(SurfaceHolder holder)
    {
        Log.i(TAG, "initCamera..");

        if(mPreviewRunning)
            mCamera.stopPreview();

        Camera.Parameters parameters;
        try {
            parameters = mCamera.getParameters();
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        parameters.setPreviewSize(videoWidth, videoHeight);
        parameters.setPictureFormat(PixelFormat.JPEG);
        parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);

        SetCameraFPS(parameters);

        setCameraDisplayOrientation(this, curCameraIndex, mCamera);

        mCamera.setParameters(parameters);

        int bufferSize = (((videoWidth|0xf)+1) * videoHeight * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat())) / 8;

        mCamera.addCallbackBuffer(new byte[bufferSize]);

        mCamera.setPreviewCallbackWithBuffer(this);
        try {
            mCamera.setPreviewDisplay(holder);
        } catch (Exception ex) {
            if (null != mCamera) {
                mCamera.release();
                mCamera = null;
            }
            ex.printStackTrace();
        }
        mCamera.startPreview();
        mCamera.autoFocus(myAutoFocusCallback);
        mPreviewRunning = true;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i(TAG, "surfaceCreated..");
        try {

            int CammeraIndex = findBackCamera();
            Log.i(TAG, "BackCamera: " + CammeraIndex);

            if (CammeraIndex == -1) {
                CammeraIndex=findFrontCamera();
                currentCameraType = FRONT;
                bt_switch_camera.setEnabled(false);
                if(CammeraIndex == -1)
                {
                    Log.i(TAG, "NO camera!!");
                    return;
                }
            } else {
                currentCameraType = BACK;
            }

            if (mCamera == null) {
                mCamera = openCamera(currentCameraType);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i(TAG, "surfaceChanged..");
        initCamera(holder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.i(TAG, "Surface Destroyed");
    }

    @Override
    protected void onDestroy() {
        Log.i(TAG, "activity destory!");

        if ( isStart ) {
            isStart = false;
            StopPublish();
            Log.i(TAG, "onDestroy StopPublish");
        }

        super.onDestroy();
        finish();

    }

    public void onConfigurationChanged(Configuration newConfig) {
        try {
            super.onConfigurationChanged(newConfig);
            Log.i(TAG, "onConfigurationChanged, start:" + isStart);
            if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                if(!isStart)
                    currentOrigentation = LANDSCAPE;
            } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
                if(!isStart)
                    currentOrigentation = PORTRAIT;
            }
        } catch (Exception ex) {
        }
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        frameCount++;
        if ( frameCount % 3000 == 0 )
        {
            Log.i("OnPre", "gc+");
            System.gc();
            Log.i("OnPre", "gc-");
        }

        if (data == null) {
            Camera.Parameters params = camera.getParameters();
            Camera.Size size = params.getPreviewSize();
            int bufferSize = (((size.width|0x1f)+1) * size.height * ImageFormat.getBitsPerPixel(params.getPreviewFormat())) / 8;
            camera.addCallbackBuffer(new byte[bufferSize]);
        }
        else
        {
            if(isStart)
            {
                libPublisher.SmartPublisherOnCaptureVideoData(data, data.length, currentCameraType, currentOrigentation);
            }
            camera.addCallbackBuffer(data);
        }
    }

    @SuppressLint("NewApi")
    private Camera openCamera(int type){
        int frontIndex = -1;
        int backIndex = -1;
        int cameraCount = Camera.getNumberOfCameras();
        Log.i(TAG, "cameraCount: " + cameraCount);

        Camera.CameraInfo info = new Camera.CameraInfo();
        for(int cameraIndex = 0; cameraIndex<cameraCount; cameraIndex++){
            Camera.getCameraInfo(cameraIndex, info);

            if(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
                frontIndex = cameraIndex;
            } else if(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK){
                backIndex = cameraIndex;
            }
        }

        currentCameraType = type;
        if(type == FRONT && frontIndex != -1){
            curCameraIndex = frontIndex;
            return Camera.open(frontIndex);
        } else if(type == BACK && backIndex != -1){
            curCameraIndex = backIndex;
            return Camera.open(backIndex);
        }
        return null;
    }

    private void switchCamera() throws IOException{
        mCamera.stopPreview();
        mCamera.release();
        if(currentCameraType == FRONT) {
            mCamera = openCamera(BACK);
        }else if(currentCameraType == BACK) {
            mCamera = openCamera(FRONT);
        }

        initCamera(mSurfaceHolder);
    }

    private void StopPublish() {
        if(audioRecord != null) {
            Log.i(TAG, "surfaceDestroyed, call StopRecording..");
            audioRecord.StopRecording();
            audioRecord = null;
        }

        if ( libPublisher != null ) {
            libPublisher.SmartPublisherStopPublish();
        }
    }

    //Check if it has front camera
    private int findFrontCamera() {
        int cameraCount = 0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();

        for (int camIdx=0; camIdx<cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                return camIdx;
            }
        }
        return -1;
    }

    //Check if it has back camera
    private int findBackCamera(){
        int cameraCount = 0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();

        for (int camIdx=0; camIdx<cameraCount;camIdx++) {
            Camera.getCameraInfo( camIdx, cameraInfo );
            if (cameraInfo.facing ==Camera.CameraInfo.CAMERA_FACING_BACK) {
                return camIdx;
            }
        }
        return -1;
    }

    private void setCameraDisplayOrientation (Activity activity, int cameraId, android.hardware.Camera camera) {
        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            // back-facing
            result = ( info.orientation - degrees + 360) % 360;
        }

        Log.i(TAG, "curDegree: "+ result);

        camera.setDisplayOrientation (result);
    }
}
